#include "device.h"
#include "f28p65x_device.h"

#include "drv_usart.h"
#include "hal/serial/serial.h"


#define __INIT_SCI_GPIO_CFG(sci, sci_reg, rx, tx)       \
    {                                                   \
        .uart_regs = (volatile struct SCI_REGS*)&sci_reg##Regs, \
        .uart_periph = sci##_BASE,                      \
        .uart_periph_clk = SYSCTL_PERIPH_CLK_##sci,     \
        .uart_rx_pin = rx,                              \
        .uart_tx_pin = tx,                              \
        .uart_rx_mux = GPIO_##rx##_##sci##_RX,          \
        .uart_tx_mux = GPIO_##tx##_##sci##_TX,          \
        .uart_rx_interrupt = INT_##sci##_RX,            \
    }

#define __INIT_UART_GPIO_CFG(uart, uart_reg, rx, tx)    \
    {                                                   \
        .uart_regs = (volatile struct SCI_REGS*)&uart_reg##Regs, \
        .uart_periph = uart##_BASE,                     \
        .uart_periph_clk = SYSCTL_PERIPH_CLK_##uart,    \
        .uart_rx_pin = rx,                              \
        .uart_tx_pin = tx,                              \
        .uart_rx_mux = GPIO_##rx##_##uart##_RX,         \
        .uart_tx_mux = GPIO_##tx##_##uart##_TX,         \
        .uart_rx_interrupt = INT_##uart,                \
    }

typedef struct serial_config_gpio {
    volatile struct SCI_REGS* uart_regs; // Pointer to the SCI register structure
    rt_uint32_t uart_periph;        // Base address of the USART
    rt_uint32_t uart_periph_clk;    // Peripheral clock for USART
    rt_uint32_t uart_rx_pin;        // GPIO number for Rx
    rt_uint32_t uart_tx_pin;        // GPIO number for Tx
    rt_uint32_t uart_rx_mux;        // GPIO Mux for Rx
    rt_uint32_t uart_tx_mux;        // GPIO Mux for Tx
    rt_uint32_t uart_rx_interrupt;  // Interrupt number for USART Rx
} serial_gpio_t;

// 因为 中断线 INT_xxx 名称格式不一致，需要区分 SCI 和 Uart
static serial_gpio_t g_uart[] = {
    /*------------------- base           rx  tx  */
    __INIT_SCI_GPIO_CFG(SCIA,    Scia,   13, 12 ),
    __INIT_SCI_GPIO_CFG(SCIB,    Scib,   11, 10 ),
    __INIT_UART_GPIO_CFG(UARTA,  Uarta,  39, 38 ),
};

static rt_err_t uart_gpio_init(struct serial_config_gpio* uart)
{
    // Configure UART RX pin
    GPIO_setPinConfig(uart->uart_rx_mux);
    GPIO_setPadConfig(uart->uart_rx_pin, GPIO_PIN_TYPE_STD);
    GPIO_setDirectionMode(uart->uart_rx_pin, GPIO_DIR_MODE_IN);
    GPIO_setQualificationMode(uart->uart_rx_pin, GPIO_QUAL_ASYNC);

    // Configure UART TX pin
    GPIO_setPinConfig(uart->uart_tx_mux);
    GPIO_setPadConfig(uart->uart_tx_pin, GPIO_PIN_TYPE_STD);
    GPIO_setDirectionMode(uart->uart_tx_pin, GPIO_DIR_MODE_OUT);
    GPIO_setQualificationMode(uart->uart_tx_pin, GPIO_QUAL_ASYNC);

    return RT_EOK;
}

static rt_err_t usart_configure(struct serial_device* serial, struct serial_configure* cfg)
{
    struct serial_config_gpio* uart;
    uint32_t uart_base;
    uint32_t baud_rate;
    uint32_t data_bit;
    uint32_t stop_bit;
    uint32_t parity;
    uint32_t config;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart = (struct serial_config_gpio*)serial->parent.user_data;
    uart_base = uart->uart_periph;
    baud_rate = cfg->baud_rate;

    uart_gpio_init(uart);

    switch (cfg->data_bits) {
    case DATA_BITS_5:
        data_bit = SCI_CONFIG_WLEN_5;
        break;
    case DATA_BITS_6:
        data_bit = SCI_CONFIG_WLEN_6;
        break;
    case DATA_BITS_7:
        data_bit = SCI_CONFIG_WLEN_7;
        break;
    case DATA_BITS_8:
        data_bit = SCI_CONFIG_WLEN_8;
        break;
    default:
        data_bit = SCI_CONFIG_WLEN_8;
        break;
    }

    switch (cfg->stop_bits) {
    case STOP_BITS_2:
        stop_bit = SCI_CONFIG_STOP_TWO;
        break;
    default:
        stop_bit = SCI_CONFIG_STOP_ONE;
        break;
    }

    switch (cfg->parity) {
    case PARITY_ODD:
        parity = SCI_CONFIG_PAR_ODD;
        break;
    case PARITY_EVEN:
        parity = SCI_CONFIG_PAR_EVEN;
        break;
    default:
        parity = SCI_CONFIG_PAR_NONE;
        break;
    }

    config = data_bit | stop_bit | parity;

    SysCtl_enablePeripheral(uart->uart_periph_clk);

    SCI_performSoftwareReset(uart_base);
    SCI_setConfig(uart_base, DEVICE_LSPCLK_FREQ, baud_rate, config);
    SCI_resetChannels(uart_base);
    SCI_clearInterruptStatus(uart_base, SCI_INT_TXFF | SCI_INT_RXFF);
    SCI_enableInterrupt(uart_base, SCI_INT_RXERR | SCI_INT_FE | SCI_INT_OE | SCI_INT_PE);
    SCI_enableFIFO(uart_base);
    SCI_setFIFOInterruptLevel(uart_base, SCI_FIFO_TX0, SCI_FIFO_RX1);
    SCI_enableModule(uart_base);
    SCI_performSoftwareReset(uart_base);

    // rt_kprintf("[%s] init: %d  %d %d %d \n", serial->parent.parent.name, baud_rate, cfg->data_bits, cfg->stop_bits, cfg->parity);

    return RT_EOK;
}

static void _close_usart(struct serial_device* serial)
{
    struct serial_config_gpio* uart = (struct serial_config_gpio*)serial->parent.user_data;
    uint32_t uart_base = uart->uart_periph;

    if (serial->parent.open_flag & RT_DEVICE_FLAG_INT_RX) {
        /* disable rx irq */
        Interrupt_disable(uart->uart_rx_interrupt);
        /* disable interrupt */
        SCI_disableInterrupt(uart_base, SCI_INT_RXFF);

        SCI_performSoftwareReset(uart_base);
    }

    if (serial->parent.open_flag & RT_DEVICE_FLAG_DMA_RX) {

    }

    if (serial->parent.open_flag & RT_DEVICE_FLAG_DMA_TX) {

    }
}

static rt_err_t usart_control(struct serial_device* serial, int cmd, void* arg)
{
    struct serial_config_gpio* uart;
    rt_uint32_t ctrl_arg = (rt_uint32_t)(arg);
    uint32_t uart_base;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct serial_config_gpio*)serial->parent.user_data;
    uart_base = uart->uart_periph;

    switch (cmd) {
    case RT_DEVICE_CTRL_CLR_INT:
        if (ctrl_arg == RT_DEVICE_FLAG_INT_RX) {
            /* disable rx irq */
            Interrupt_disable(uart->uart_rx_interrupt);
            /* disable interrupt */
            SCI_disableInterrupt(uart_base, SCI_INT_RXFF);
        }
        break;

    case RT_DEVICE_CTRL_SET_INT:
        if (ctrl_arg == RT_DEVICE_FLAG_INT_RX) {
            SCI_clearInterruptStatus(uart_base, SCI_INT_RXFF);
            SCI_enableFIFO(uart_base);
            SCI_setFIFOInterruptLevel(uart_base, SCI_FIFO_TX0, SCI_FIFO_RX1);

            SCI_enableInterrupt(uart_base, SCI_INT_RXFF);
            Interrupt_enable(uart->uart_rx_interrupt);
        }
        break;

        /* USART DMA config */
    case RT_DEVICE_CTRL_CONFIG:
        if (ctrl_arg == RT_DEVICE_FLAG_DMA_RX) {
            struct serial_rx_fifo* rx_fifo = (struct serial_rx_fifo*)serial->serial_rx;

        }

        if (ctrl_arg == RT_DEVICE_FLAG_DMA_TX) {

        }
        break;

    case RT_DEVICE_CTRL_SUSPEND:
        _close_usart(serial);
        break;

    default:
        break;
    }

    return RT_EOK;
}

static int usart_putc(struct serial_device* serial, char ch)
{
    RT_ASSERT(serial != RT_NULL);

    struct serial_config_gpio* uart = (struct serial_config_gpio*)serial->parent.user_data;
    uint32_t uart_base = uart->uart_periph;

    uart->uart_regs->SCITXBUF.bit.TXDT = ch;

    /* wait transmit finish */
    while (uart->uart_regs->SCIFFTX.bit.TXFFST != 0)
        ;

    return RT_EOK;
}

static int usart_getc(struct serial_device* serial)
{
    int ch = -1;

    RT_ASSERT(serial != RT_NULL);

    struct serial_config_gpio* uart = (struct serial_config_gpio*)serial->parent.user_data;
    uint32_t uart_base = uart->uart_periph;

    if (0 != uart->uart_regs->SCIFFRX.bit.RXFFST) {
        ch = uart->uart_regs->SCIRXBUF.bit.SAR;
    }

    return ch;
}

static rt_size_t usart_dma_transmit(struct serial_device* serial, rt_uint8_t* buf, rt_size_t size, int direction)
{
    if (direction == SERIAL_DMA_TX) {
        // _dma_transmit(serial->parent.user_data, buf, size);
        // return size;
    }

    return 0;
}

/*-------------------------------------------------------------*/
/* usart driver operations */
static const struct usart_ops __usart_ops = {
    .configure = usart_configure,
    .control = usart_control,
    .putc = usart_putc,
    .getc = usart_getc,
    .dma_transmit = usart_dma_transmit, // Not implemented
};

static struct serial_device serial0;
static struct serial_device serial1;
static struct serial_device serial2;

rt_err_t drv_usart_init(void)
{
    rt_err_t rt_err = RT_EOK;

    struct serial_configure config0 = SERIAL_DEFAULT_CONFIG_0;
    struct serial_configure config1 = SERIAL_DEFAULT_CONFIG_0;
    struct serial_configure config2 = SERIAL_DEFAULT_CONFIG_0;

    /*--------------------- serial0: SCIA: 57600  8N1  Debug */
    serial0.ops = &__usart_ops;
    serial0.config = config0;

    rt_err |= hal_serial_register(&serial0,
                                  "serial0",
                                  RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                                  &g_uart[0]);

    /*--------------------- serial1: SCIB: 57600  8N1  GPS */
    serial1.ops = &__usart_ops;
    serial1.config = config1;

    rt_err |= hal_serial_register(&serial1,
                                  "serial1",
                                  RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                                  &g_uart[1]);

    /*--------------------- serial2: UARTA: 57600  8N1  TeleMetry */
    serial2.ops = &__usart_ops;
    serial2.config = config2;

    rt_err |= hal_serial_register(&serial2,
                                  "serial2",
                                  RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                                  &g_uart[2]);

    return rt_err;
}

static void uart_isr(struct serial_device* serial)
{
    struct serial_config_gpio* uart = (struct serial_config_gpio*)serial->parent.user_data;
    uint32_t uart_base = uart->uart_periph;

    rt_uint32_t int_flag = SCI_getInterruptStatus(uart_base);

    if (!(int_flag & SCI_RXST_RXERROR)) {
        /* high-level ISR routine */
        hal_serial_isr(serial, SERIAL_EVENT_RX_IND);
    }
}

__interrupt void scia_rx_isr(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
    /* uart isr routine */
    uart_isr(&serial0);
    /* leave interrupt */
    rt_interrupt_leave();
}

__interrupt void scib_rx_isr(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
    /* uart isr routine */
    uart_isr(&serial1);
    /* leave interrupt */
    rt_interrupt_leave();
}

__interrupt void uarta_isr(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
    /* uart isr routine */
    uart_isr(&serial2);
    /* leave interrupt */
    rt_interrupt_leave();
}
